home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 12 / Cream of the Crop 12 (Part II) / Cream of the Crop 12 (Part II).iso / OS2 / BLT2_205.ZIP / src / blt2cx08.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-18  |  17.8 KB  |  602 lines

  1. /* 
  2.  *
  3.  * blt2cx08.c - 17-Oct-1995 Cornel Huth 
  4.  * This module is called by blt2demo.c
  5.  * Locks
  6.  *
  7.  * File-Open locks are not demonstrated here since they typically are not
  8.  * used for online transaction processing since these locks require a file
  9.  * open/close to change the lock type.  The locks demonstrated are region
  10.  * locks/relocks, where physical regions of the file are locked for access,
  11.  * and not the file open itself (other apps can open at any time).
  12.  *
  13.  * This example use LOCK_XB, RELOCK_XB, and UNLOCK_XB, which are transaction-
  14.  * list routines where a group of related files are all locked in one atomic
  15.  * operation (if all succeed all are locked, if one cannot be locked, none 
  16.  * are locked).  Each individual file can be locked on its own by using the
  17.  * UN/RE/LOCK_INDEX_XB and UN/RE/LOCK_DATA_XB routines.
  18.  *
  19.  * RELOCK_XB, RELOCK_INDEX_XB, and RELOCK_DATA_XB are not supported in 
  20.  * W95 systems (Win/NT does support SHARED region locks, as OS/2, but does
  21.  * not support atomic relocking).
  22.  *
  23.  * RELOCK_XB, RELOCK_INDEX_XB, and RELOCK_DATA_XB are not supported in
  24.  * DOSX systems (DOS doesn't support this feature).
  25.  *
  26.  */
  27.  
  28. #include "platform.h"
  29.  
  30. #ifdef ON_OS2
  31.    #include <os2.h>
  32. #endif
  33. #ifdef ON_W95
  34.    #define WIN32_LEAN_AND_MEAN
  35.    #include <windows.h>
  36. #endif
  37.  
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <time.h>
  41. #include <string.h>
  42.  
  43. #ifdef ON_OS2
  44.    #include "bullet2.h"
  45. #endif
  46. #ifdef ON_W95
  47.    #include "bullet95.h"
  48. #endif
  49. #ifdef ON_DOSX
  50.    #include "bulletx.h"
  51. #endif
  52.  
  53.  
  54.  
  55. void cx08BuildFieldList(FIELDDESCTYPE fieldList[]);
  56.  
  57. extern CHAR *collateTable;
  58.  
  59.  
  60. int cx08(void) {
  61.  
  62. #pragma pack(1)
  63.  
  64. ACCESSPACK AP[2];
  65. DOSFILEPACK DFP;
  66. CREATEDATAPACK CDP;
  67. CREATEINDEXPACK CIP;
  68. HANDLEPACK HP;
  69. LOCKPACK LP[2];
  70. OPENPACK OP;
  71.  
  72. struct EmpRecType {
  73.  CHAR tag;              // record tag, init to SPACE, * means deleted
  74.  CHAR empID[9];         // SSN (not 0T string)
  75.  CHAR empLN[16];        // last name
  76.  CHAR empFN[16];        // first name
  77.  CHAR empHire[8];       // "YYYYMMDD" (not 0T string)
  78.  CHAR empDept[6];       // department assigned
  79. }; // 56 bytes
  80. struct EmpRecType EmpRec;
  81.  
  82. #pragma pack()
  83.  
  84. LONG rez;                       // return value from Bullet
  85. LONG rezX;                      // converted pack index (pack that failed ID)
  86. LONG isLocked;                  // flag indicating active locks
  87.  
  88. CHAR nameData[]="$CX08.DBF";    // data filename
  89. ULONG dataID=0;                 // handles of data file
  90. FIELDDESCTYPE fieldList[5];     // 5 fields used in data record 
  91.  
  92. CHAR *nameIndex[] = {
  93. "$CX08A.IX3",                   // first index file
  94. "$CX08B.IX3"                    // second...
  95. };
  96. ULONG indexID[] ={0,0};
  97. CHAR *keyExpression[] = {
  98. "SSN",
  99. "SUBSTR(LNAME,1,4)+SUBSTR(SSN,6,4)",
  100. };
  101. CHAR *keyBuffer[2][68];
  102.  
  103.  
  104. setbuf(stdout,NULL);
  105. printf("(Self-running module)\n\n");
  106.  
  107. memset(fieldList,0,sizeof(fieldList));  // init unused bytes to 0 (required)
  108. cx08BuildFieldList(fieldList);
  109.  
  110. // Delete previous files from any previous run (disregard any error return)
  111.  
  112. DFP.func = DELETE_FILE_DOS;
  113. DFP.filenamePtr = nameData;
  114. rez = BULLET(&DFP);
  115. DFP.filenamePtr = nameIndex[0];
  116. rez = BULLET(&DFP);
  117. DFP.filenamePtr = nameIndex[1];
  118. rez = BULLET(&DFP);
  119.  
  120. printf("Creating one data file ... ");
  121.  
  122. // Create the data file, a standard DBF (ID=3) as defined in fieldList above.
  123.  
  124. CDP.func = CREATE_DATA_XB;
  125. CDP.filenamePtr = nameData;
  126. CDP.noFields = 5;
  127. CDP.fieldListPtr = fieldList;
  128. CDP.fileID = 0x03;
  129. rez = BULLET(&CDP);
  130. if (rez) {
  131.    printf("Failed data file create.  Err: %li\n",rez);
  132.    goto Abend;
  133. }
  134.  
  135. // Open the data file
  136.  
  137. printf("opening ... ");
  138. OP.func = OPEN_DATA_XB;
  139. OP.filenamePtr = nameData;
  140. OP.asMode = READWRITE | DENYNONE;
  141. rez = BULLET(&OP);
  142. if (rez) {
  143.    printf("Failed data file open.  Err: %li\n",rez);
  144.    goto Abend;
  145. }
  146. dataID=OP.handle;
  147.  
  148. // Create index files
  149.  
  150. printf("Done.\nCreating two index files ... ");
  151. CIP.func = CREATE_INDEX_XB;
  152. CIP.filenamePtr = nameIndex[0];
  153. CIP.keyExpPtr = keyExpression[0];
  154. CIP.xbLink = dataID;        
  155. CIP.sortFunction = ASCII_SORT;
  156. CIP.codePage = CODEPAGE;
  157. CIP.countryCode = CTRYCODE;
  158. CIP.collatePtr = NULL;
  159. CIP.nodeSize = 512;
  160. rez = BULLET(&CIP);
  161. if (rez) {
  162.    printf("Failed first index file create.  Err: %li\n",rez);
  163.    goto Abend;
  164. }
  165.  
  166. CIP.filenamePtr = nameIndex[1];
  167. CIP.keyExpPtr = keyExpression[1];
  168. CIP.xbLink = dataID;        
  169. CIP.sortFunction = NLS_SORT;
  170. CIP.codePage = CODEPAGE;
  171. CIP.countryCode = CTRYCODE;
  172. CIP.collatePtr = collateTable;
  173. CIP.nodeSize = 512;
  174. rez = BULLET(&CIP);
  175. if (rez) {
  176.    printf("Failed second index file create.  Err: %li\n",rez);
  177.    goto Abend;
  178. }
  179.  
  180. // Open the index files
  181.  
  182. printf("opening ... ");
  183. OP.func = OPEN_INDEX_XB;
  184. OP.filenamePtr = nameIndex[0];
  185. OP.asMode = READWRITE | DENYNONE;
  186. OP.xbLink = dataID;
  187. rez = BULLET(&OP);
  188. if (rez) {
  189.    printf("Failed first index file open.  Err: %li\n",rez);
  190.    goto Abend;
  191. }
  192. indexID[0] = OP.handle;
  193.  
  194. OP.filenamePtr = nameIndex[1];
  195. OP.xbLink = dataID;
  196. rez = BULLET(&OP);
  197. if (rez) {
  198.    printf("Failed second index file open.  Err: %li\n",rez);
  199.    goto Abend;
  200. }
  201. indexID[1] = OP.handle;
  202. printf("Done.\n\n");
  203.  
  204. // Lock as one would do in preparation for an update, or any operation
  205. // where the database needs to be changed, or a status needs to be queried
  206. // Note: when using the list-processing lock routines (LOCK_XB, UNLOCK_XB,
  207. //       and RELOCK_XB) only index file handles are used in LP.handle --
  208. //       their data file will be known internally and locked accordingly.
  209. //
  210. // Since index files only are listed, you may wonder how redudant locks
  211. // are avoid.  The answer is that Bullet locks only the first time, thereafter
  212. // a lock count is maintained (on a handle basis).  So, while this example
  213. // does indeed "lock" the data file twice, only the first LP actually does
  214. // anything regarding the data file (both index files relate to the same 
  215. // data file in this example); the second LP data lock mearly increments
  216. // the lock count on the data file handle (the first encounter actually
  217. // locks the data region and reloads the data header).  Since each index
  218. // file is separate, there are indeed separate physical locks done to each
  219. // index file (and each maintain separate lock counts, header reloads, etc.).
  220.  
  221. printf("Locking the database (exclusive locks) ... ");
  222. isLocked = 0;
  223.  
  224. LP[0].func = LOCK_XB;
  225. LP[0].handle = indexID[0];
  226. LP[0].xlMode = 0;       // as are index locks
  227. LP[0].dlMode = 0;       // data locks are exclusive (initially)
  228. LP[0].recStart = 0;     // data file's entire region is locked
  229. LP[0].recCount = 0;     // might as well (complete and all that)
  230. LP[0].nextPtr = &LP[1];
  231. LP[1].handle = indexID[1];
  232. LP[1].xlMode = 0;
  233. LP[1].dlMode = 0;
  234. LP[1].recStart = 0;
  235. LP[1].recCount = 0;
  236. LP[1].nextPtr = NULL;
  237. rez = BULLET(&LP[0]);
  238. if (rez) {
  239.  
  240.    // since this transaction routine deals with both index and data files
  241.    // rez is returned as the pack that failed: a negative number if the
  242.    // failure was data file related, and positive if index file related
  243.    // in any case, rez is not "the" error code, but is the index of the
  244.    // pack that failed (first pack being pack 1), and in the pack's .stat 
  245.    // lies the true error code
  246.  
  247.    if (rez < 0) {              // data related failure
  248.       rezX = abs(rez)-1;       // minus 1 since C arrays are 0-based
  249.       rez = LP[rezX].stat;     // rez is true error code
  250.       printf("Lock failed on data, LP[%d], err: %li\n",rezX,rez);
  251.       goto Abend;
  252.    }
  253.    else {
  254.       rezX = rez-1;
  255.       rez = LP[rezX].stat;
  256.       printf("Lock failed on data, LP[%d], err: %li\n",rezX,rez);
  257.       goto Abend;
  258.    }
  259. }         
  260.  
  261. // If it gets here, locks are in place
  262. // there is no partial locking with LOCK_XB -- it either all succeeds or
  263. // those locks that were successful in the call are backed out
  264.  
  265. printf("Done.\n\n");
  266. isLocked=1;
  267.  
  268. // At this point, the database (the three files in this case) can be
  269. // written to without concern of corruption (i.e., only this process
  270. // can write OR read to these files).  Assume that that has taken place...
  271.  
  272. printf("INSERT_XB one item into database ... ");
  273. EmpRec.tag = ' ';
  274. strncpy(EmpRec.empID,"000000001",9);
  275. strcpy(EmpRec.empLN,"LockLastName");
  276. strcpy(EmpRec.empFN,"LockFirstName");
  277. strncpy(EmpRec.empHire,"19950618",8);
  278. strcpy(EmpRec.empDept,"XYZ");
  279.  
  280. // Inserts only the single item (adds a record, inserts a key into each index)
  281.  
  282. AP[0].func = INSERT_XB;
  283. AP[0].handle = indexID[0];
  284. AP[0].recNo = 0;                // required
  285. AP[0].recPtr = &EmpRec;
  286. AP[0].keyPtr = keyBuffer[0];
  287. AP[0].nextPtr = &AP[1];
  288. AP[1].func = INSERT_XB;
  289. AP[1].handle = indexID[1];
  290. AP[1].recNo = 0x80000000;       // this pack using record added in first pack
  291. AP[1].recPtr = &EmpRec;
  292. AP[1].keyPtr = keyBuffer[1];
  293. AP[1].nextPtr = NULL;
  294. rez = BULLET(&AP[0]);
  295. if (rez) {
  296.    if (rez < 0) {
  297.       rez = abs(rez);
  298.       printf("INSERT_XB failed, data AP[%d], err: %d\n",rez-1,AP[rez-1].stat);
  299.    } 
  300.    else {
  301.       printf("INSERT_XB failed, index AP[%d], err: %d\n",rez-1,AP[rez-1].stat);
  302.    }
  303.    goto Abend;
  304. }
  305.  
  306. #ifdef ON_OS2 // since RELOCK_XB is not supported in DOSX or WIN95 (WIN/NT does)
  307.  
  308. // Assume that the insert was successful (it was), and now you want to allow
  309. // other processes to be able to read the database, but you do not want
  310. // the to be able to change it (not uncommon).  OS/2 offers two locking
  311. // modes: 1) exclusive locks, where no other process may read or write
  312. // to the region locked except the current process (the one locking it),
  313. // and 2) shared locks, where any other process may READ the locked region,
  314. // not NO process, INCLUDING the current process, may write to the region.
  315. // This feature is not available in MS-DOS, except at the file open lock
  316. // level (but file open locks are too slow to be generally useful).  
  317. // Relock is also an atomic operation, in that there is no chance that
  318. // another process can steal away the locks during the relock.
  319. // Note that you can have the initial lock state set to 'shared', rather
  320. // that exclusive as this example, and then relock to 'exclusive', however
  321. // you can only call RELOCK_XB if a prior full lock (LOCK_XB) is in force.
  322.  
  323. // Note:  When switching from exclusive to shared locks, Bullet automatically
  324. //        flushes the file (index or data).  This to ensure that a flush
  325. //        does take place while write-permission is still available -- for
  326. //        example, you can close files with shared locks last active, and
  327. //        Bullet does not flush (flush=writes file header, commits to OS)
  328. //        the file, and cannot, since it can only flush when it has an active
  329. //        lock, and that lock mode must permit writing (shared does not). 
  330. //        Therefore, whenever you switch from exclusive lock to shared lock,
  331. //        Bullet flushes the file (which may do nothing if no flush is needed).
  332.  
  333. // Since LP[] was previously set, only the changed parms need to be set.
  334. // Be sure to set each LP[].func in the pack to the new operation, though.
  335.  
  336. printf("Done.\n\nChanging locks from exclusive to shared, relock ... ");
  337. LP[0].func = RELOCK_XB;
  338. LP[0].xlMode = 1;       // as are index locks
  339. LP[0].dlMode = 1;       // data locks are now shared
  340. LP[1].func = RELOCK_XB;
  341. LP[1].xlMode = 1;       // (also see the Note: above, regarding flushing)
  342. LP[1].dlMode = 1;
  343. rez = BULLET(&LP[0]);
  344. if (rez) {
  345.    if (rez < 0) {
  346.       rezX = abs(rez)-1;
  347.       rez = LP[rezX].stat;
  348.       printf("Relock failed on data, LP[%d], err: %li\n",rezX,rez);
  349.       goto Abend;
  350.    }
  351.    else {
  352.       rezX = rez-1;
  353.       rez = LP[rezX].stat;
  354.       printf("Relock failed on index, LP[%d], err: %li\n",rezX,rez);
  355.       goto Abend;
  356.    }
  357. }         
  358.  
  359. printf("Done.\n\n...Attempting to write to the database (this will FAIL):\n");
  360.  
  361. // At this point, any process may access the database and read from it,
  362. // but no process, not even this one, may write to it so long as the 
  363. // shared locks are in place.
  364.  
  365. // *Attempts* to inserts into the database but it will fail because shared
  366. // locks permit read-only access for all processes, even the locking one
  367. // The data record, EmpRec, is changed slightly so that an ERR_KEY_EXISTS
  368. // error is not returned (only a read is needed to get that far)
  369.  
  370. strncpy(EmpRec.empID,"000000002",9); // change keys built (see above)
  371.  
  372. AP[0].func = INSERT_XB;         
  373. AP[0].handle = indexID[0];
  374. AP[0].recNo = 0;                // required (not that this also returns a value)
  375. AP[0].recPtr = &EmpRec;
  376. AP[0].keyPtr = keyBuffer[0];
  377. AP[0].nextPtr = &AP[1];
  378. AP[1].func = INSERT_XB;
  379. AP[1].handle = indexID[1];
  380. AP[1].recNo = 0x80000000;       // this pack using record added in first pack
  381. AP[1].recPtr = &EmpRec;
  382. AP[1].keyPtr = keyBuffer[1];
  383. AP[1].nextPtr = NULL;
  384. rez = BULLET(&AP[0]);
  385. if (rez) {
  386.    if (rez < 0) {
  387.       rez = abs(rez);
  388.       printf("Sure enough, INSERT_XB failed, data AP[%d], err: %d\n",rez-1,AP[rez-1].stat);
  389.    } 
  390.    else
  391.       printf("Sure enough, INSERT_XB failed, index AP[%d], err: %d\n",rez-1,AP[rez-1].stat);
  392. }
  393. else {
  394.    printf("\nThat write should not have worked!  See source.\n");
  395.    goto Abend;
  396. }
  397.  
  398. // switch back to exclusive locks and now write the second item
  399.  
  400. printf("\nChanging locks from shared back to exclusive, relock ... ");
  401. LP[0].func = RELOCK_XB;
  402. LP[0].xlMode = 0;
  403. LP[0].dlMode = 0;
  404. LP[1].func = RELOCK_XB;
  405. LP[1].xlMode = 0;
  406. LP[1].dlMode = 0;
  407. rez = BULLET(&LP[0]);
  408. if (rez) {
  409.    if (rez < 0) {
  410.       rezX = abs(rez)-1;
  411.       rez = LP[rezX].stat;
  412.       printf("Relock failed on data, LP[%d], err: %li\n",rezX,rez);
  413.       goto Abend;
  414.    }
  415.    else {
  416.       rezX = rez-1;
  417.       rez = LP[rezX].stat;
  418.       printf("Relock failed on index, LP[%d], err: %li\n",rezX,rez);
  419.       goto Abend;
  420.    }
  421. }         
  422.  
  423. printf("Done.\n\n...Attempting to write to the database (this will WORK) ... ");
  424.  
  425. AP[0].func = INSERT_XB;         
  426. AP[0].handle = indexID[0];
  427. AP[0].recNo = 0;
  428. AP[0].recPtr = &EmpRec;
  429. AP[0].keyPtr = keyBuffer[0];
  430. AP[0].nextPtr = &AP[1];
  431. AP[1].func = INSERT_XB;
  432. AP[1].handle = indexID[1];
  433. AP[1].recNo = 0x80000000;
  434. AP[1].recPtr = &EmpRec;
  435. AP[1].keyPtr = keyBuffer[1];
  436. AP[1].nextPtr = NULL;
  437. rez = BULLET(&AP[0]);
  438. if (rez) {
  439.    if (rez < 0) {
  440.       rez = abs(rez);
  441.       printf("Bzzt! INSERT_XB failed, data AP[%d], err: %d\n",rez-1,AP[rez-1].stat);
  442.    } 
  443.    else {
  444.       printf("Bzzt! INSERT_XB failed, index AP[%d], err: %d\n",rez-1,AP[rez-1].stat);
  445.    }
  446.    goto Abend;
  447. }
  448. else 
  449.    printf("Done.\n");
  450.  
  451.  
  452. #else
  453.  
  454.    printf("Done.\n");   // DOSX and Win95 do NOT support RELOCK_XB
  455.  
  456. #endif
  457.  
  458.  
  459. // Fatal errors above come straight to here
  460. Abend:
  461.  
  462. // Consider all processing done, and you want to finish up database access
  463. // The operating system will release any locks on files when the files
  464. // are closed, however, it is only good practice to ensure that you perform
  465. // this task, if for no other reason than to make sure they are unlocked!
  466. // As with the previos call, all LP[] parms are already set but .func.
  467. // Also note that when unlocking, the .xlMode/.dlMode must be set to the 
  468. // current real state -- in other words, since .xlMode=.dlMode=1 at this
  469. // point (set immediately above), when unlocking these two parms should
  470. // remain the same.  The reason is that Bullet uses these parms to determine
  471. // if a flush is necessary.  If they were 1, then the last access could only
  472. // have been read-only, and so not flush is necessary (also, another process
  473. // may STILL have a shared lock on the file, so a flush would therefor fail!).
  474. // If 0, then the standard flush operation is performed.
  475.  
  476. printf("\nUnlocking the database (exclusive locks) ... ");
  477. if (isLocked) {
  478.    LP[0].func = UNLOCK_XB;
  479.    LP[1].func = UNLOCK_XB;
  480.    rez = BULLET(&LP[0]);
  481.    if (rez) {
  482.       if (rez < 0) {
  483.          rezX = abs(rez)-1;
  484.          rez = LP[rezX].stat;
  485.          printf("Unlock failed on data, LP[%d], err: %li\n",rezX,rez);
  486.       }
  487.       else {
  488.          rezX = rez-1;
  489.          rez = LP[rezX].stat;
  490.          printf("Unlock failed on index, LP[%d], err: %li\n",rezX,rez);
  491.       }
  492.    }
  493. }
  494.  
  495. printf("Done.\n\n");
  496.  
  497. // Note that if there is an error in the unlock process, you may need to
  498. // manually unlock those packs that lie beyond the failed pack.  Unlikely
  499. // is such to happen, though.
  500.  
  501. if (rez==0) 
  502.    isLocked=0;
  503.  
  504. // Close files (index files first then data (recommended but not required))
  505.  
  506. printf("Closing files ... ");
  507. if (indexID[0]) {
  508.    HP.func = CLOSE_INDEX_XB;
  509.    HP.handle = indexID[0];
  510.    rez = BULLET(&HP);
  511.    if (rez)
  512.       printf("Failed first index file close.  Err: %li\n",rez);
  513. }
  514.  
  515. if (indexID[1]) {
  516.    HP.func = CLOSE_INDEX_XB;
  517.    HP.handle = indexID[1];
  518.    rez = BULLET(&HP);
  519.    if (rez)
  520.       printf("Failed second index file close.  Err: %li\n",rez);
  521. }
  522.  
  523. if (dataID) {
  524.    HP.func = CLOSE_DATA_XB;
  525.    HP.handle = dataID;
  526.    rez = BULLET(&HP);
  527.    if (rez)
  528.       printf("Failed data file close.  Err: %li\n",rez);
  529. }
  530. printf("Done.\n");
  531. return rez;  // module exit (well, rez isn't that interesting at this point)
  532. }
  533.  
  534.  
  535. //------------------------------------
  536. // Init field list items for data file
  537.  
  538. void cx08BuildFieldList(FIELDDESCTYPE fieldList[]) {
  539.  
  540. strcpy(fieldList[0].fieldName, "SSN");  // field names must be upper-case
  541. fieldList[0].fieldType = 'C';           // field types must be upper-case
  542. fieldList[0].fieldLen = 9;
  543. fieldList[0].fieldDC = 0;
  544.  
  545. strcpy(fieldList[1].fieldName, "LNAME");
  546. fieldList[1].fieldType = 'C';
  547. fieldList[1].fieldLen = 16;
  548. fieldList[1].fieldDC = 0;
  549.  
  550. strcpy(fieldList[2].fieldName, "FNAME");
  551. fieldList[2].fieldType = 'C';
  552. fieldList[2].fieldLen = 16;
  553. fieldList[2].fieldDC = 0;
  554.  
  555. strcpy(fieldList[3].fieldName, "HIRED");
  556. fieldList[3].fieldType = 'D';
  557. fieldList[3].fieldLen = 8;      // date field type must be 8.0
  558. fieldList[3].fieldDC = 0;
  559.  
  560. strcpy(fieldList[4].fieldName, "DEPT");
  561. fieldList[4].fieldType = 'C';
  562. fieldList[4].fieldLen = 6;
  563. fieldList[4].fieldDC = 0;
  564. }
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.  
  602.